#include <cstdio>
#include <vector>

#include <string>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <random>


#include "avl.h"
#include "avl_test.h"


#include <nlohmann/json.hpp>


#include <CUnit/CUnit.h>
using json = nlohmann::json;


namespace ns {
      

struct Person {


    bool operator== (const Person &p) const {

        bool res{true};
        if ( name != p.name) {
            std::cout << "name not eq" << "\n";
            res = false;
        }
        if ( phone_number != p.phone_number) {
            std::cout << "phone_number not eq" << "\n";
            res = false;
        }
        if ( age != p.age) {
            std::cout << "age not eq" << "\n";
            res = false;
        }
        return res;
    }


    void echo() const {
        std::cout << "name: " << name << "\n";
        std::cout << "phone_number: " << phone_number << "\n";
        std::cout << "age: " << age << "\n";
    }
    
   
    std::string name{};
    std::string phone_number{};
    int age{};


    
};

// void to_json(json& j, const Person& p) {
//     j = json{{"name", p.name_}, {"phone_number", p.phone_number_}, {"age", p.age_}};
// }
// void from_json(const json& j, Person& p) {
//         j.at("name").get_to(p.name_);
//         j.at("phone_number").get_to(p.phone_number_);
//         j.at("age").get_to(p.age_);
// }
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Person, name, phone_number, age)
}


bool add_test_case_to_json(
    std::string suite_name, avl_test_case tcase,  
    nlohmann::json& suites_json)
{
    bool have_suite{};
    bool have_case{};
    bool is_new_case{};

    for (auto &suite: suites_json) {
        if ( suite["suite_name"] == suite_name) {

            have_suite = true;

            auto suite_cases = suite["cases"].get<std::vector<avl_test_case>>();
            
            bool have_case{};
            for (auto &scase: suite_cases) {
                if (scase.isEqual(tcase)) {
                    have_case = true;
                    break;
                }
            }

            if ( not have_case) {
                //std::cout << "old suite: "<<suite_name <<" add new case" << std::endl;
                nlohmann::json& cases_json = suite["cases"];

                
                nlohmann::json ncase_json = tcase;
                cases_json.push_back( ncase_json);
                is_new_case = true;
            }
            break;
        }
    }

    if (not have_suite) {

        avl_test_suite nsuite{};
        nsuite.suite_name = suite_name;
        nsuite.cases.emplace_back(tcase);

        nlohmann::json nsuite_json = nsuite;
        suites_json.push_back( nsuite_json);
        is_new_case = true;
    }

    if ( is_new_case) {
        std::cout << suite_name << " add a new case\n";
    }
    else {
        std::cout << suite_name << " already have a case\n";
    }

    return is_new_case;
}

void test_insert_noRotate(AVLTestManager &mng)
{
    std::vector<int> val_seqs{2,1,3};
    std::vector<int> op_seqs{0,0,0}; 

    avl_test_case tcase{{2,1,3},{{AVL_INSERT,3}}, {1,2,3}};

    std::string str{__func__};


    mng.addTestCase(str, tcase);
    // add_test_case_to_json( str, tcase, suites_json);

    // test_format( val_seqs, op_seqs);
}

void test_insert_lll(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json) 
{

    std::vector<int> val_seqs{3,2,1};
    std::vector<int> op_seqs{0,0,0};

    std::string str{__func__};
    avl_test_case tcase{{3,2,1}, {{AVL_INSERT,3}}, {1,2,3}};

    // test_format( val_seqs, op_seqs);

    add_test_case_to_json( str, tcase, suites_json);
}

void test_insert_rrr(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json) 
{
    std::vector<int> val_seqs{4,5,6};
    std::vector<int> op_seqs{0,0,0}; 
    // test_format( val_seqs, op_seqs);


    std::string str{__func__};
    avl_test_case tcase{{4,5,6}, {{AVL_INSERT,3}}, {4,5,6}};


    add_test_case_to_json( str, tcase, suites_json);
}
void test_insert_lr(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs{80,20,40};
    std::vector<int> op_seqs{0,0,0}; 


     std::string str{__func__};
    avl_test_case tcase{ {80,20,40}, {{AVL_INSERT,3}}, {20,40,80}};
    

    add_test_case_to_json( str, tcase, suites_json);
    //test_format( val_seqs, op_seqs);
}

void test_insert_rl(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{

    std::vector<int> val_seqs { 100,300,121 };
    std::vector<int> op_seqs{0,0,0}; 
    
    std::string str{__func__};
    avl_test_case tcase{ {100,300,211}, {{AVL_INSERT,3}}, {100,211,300}};


    add_test_case_to_json( str, tcase, suites_json);
    // test_format( val_seqs, op_seqs);
}

void test_insert_same(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 10,20,10 };
    std::vector<int> op_seqs{0,0,0}; 
   

    std::string  str{__func__};
    avl_test_case tcase{ {10,20,10}, {{AVL_INSERT,3}}, {10,20}};


    add_test_case_to_json( str, tcase,  suites_json);
}

void test_firstL_thenR(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 1,2,3,0,-1 };
    std::vector<int> op_seqs( val_seqs.size(), AVL_INSERT); 


    std::string  str{__func__};
    avl_test_case tcase{ {1,2,3,0,-1}, {{AVL_INSERT,5}}, {-1,0,1,2,3}};


    add_test_case_to_json( str, tcase, suites_json);

    // test_format( val_seqs, op_seqs);
}
void test_firstR_thenL(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{

    std::vector<int> val_seqs { 8, 2, 1, 9, 10};
    std::vector<int> op_seqs( val_seqs.size(), AVL_INSERT); 
    // test_format( val_seqs, op_seqs);

    std::string str{__func__};
    avl_test_case tcase{ {8,2,1,9,10}, {{AVL_INSERT,5}}, {1,2,8,9,10}};

    add_test_case_to_json( str, tcase, suites_json);
}
void test_RotateR_twice(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 8, 2, 1, 5, 4};
    std::vector<int> op_seqs( val_seqs.size(), AVL_INSERT); 

    std::string str{__func__};
    avl_test_case tcase{ {8,2,1,5,4}, {{AVL_INSERT,5}}, {1,2,4,5,8}};


    add_test_case_to_json( str, tcase,  suites_json);
    // ( val_seqs, op_seqs);
}
void test_RotateL_twice(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 1, 2, 3, 4, 5};
    std::vector<int> op_seqs( val_seqs.size(), AVL_INSERT); 
    
    std::string str{__func__};
    avl_test_case tcase{ {1,2,3,4,5},{{AVL_INSERT,5}}, {1,2,3,4,5}};

    add_test_case_to_json( str, tcase,  suites_json);
    
    //test_format( val_seqs, op_seqs);
}

void test_delete_root(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{   
    std::vector<int> val_seqs { 1, 2, 3, 2};
    std::vector<int> op_seqs( val_seqs.size() - 1, static_cast<int>(AVL_INSERT));
    op_seqs.push_back( static_cast<int>(AVL_DEL));

    avl_test_case tcase{ {1,2,3,2}, {{AVL_INSERT,3},{AVL_DEL,1}}, {1,3}};
    std::string str{__func__};

    add_test_case_to_json( str, tcase, suites_json);
    // test_format( val_seqs, op_seqs);
}
void test_delete_and_RotateL(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 1, 2, 5, 4, 7, 1};
    std::vector<int> op_seqs( val_seqs.size() - 1, static_cast<int>(AVL_INSERT));
    op_seqs.push_back( static_cast<int>(AVL_DEL));
    

    avl_test_case tcase{ {1,2,5,4,7,1}, {{AVL_INSERT,5}, {AVL_DEL,1}}, {2,4,5,7}};
    std::string str{__func__};

    add_test_case_to_json( str, tcase,  suites_json);
    //test_format( val_seqs, op_seqs);
}

void test_delete_and_RotateR(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{   
    std::vector<int> val_seqs { 7,9, 10, 5, 6, 10};
    std::vector<int> op_seqs( val_seqs.size() - 1, static_cast<int>(AVL_INSERT));
    op_seqs.push_back( static_cast<int>(AVL_DEL));
    // test_format( val_seqs, op_seqs);

     avl_test_case tcase2{ {33, 13, 53, 9, 21, 61, 8, 11, 13}, {{AVL_INSERT,8}, {AVL_DEL,1}}, {8,9,11,21,33,53,61}};
    avl_test_case tcase1{ {7,9,10,5,6,10}, {{AVL_INSERT, 5}, {AVL_DEL, 1}}, {5,6,7,9}};
    std::string str{__func__};

    add_test_case_to_json( str, tcase1,  suites_json);
    add_test_case_to_json( str, tcase2,  suites_json);


    std::vector<int> v2{ 33, 13, 53, 9, 21, 61, 8, 11, 13};
    std::vector<int> o2( v2.size() - 1, static_cast<int>(AVL_INSERT));
    o2.push_back( static_cast<int>(AVL_DEL));
    // test_format( v2, o2);
}

void test_delete_and_RotateLR(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 90, 70, 100, 80,  100};
    std::vector<int> op_seqs( val_seqs.size() - 1, static_cast<int>(AVL_INSERT));
    op_seqs.push_back( static_cast<int>(AVL_DEL));


    avl_test_case tcase{ { 90, 70, 100, 80, 100}, {{AVL_INSERT, 4}, {AVL_DEL,1}}, {70, 80,  90}};

    std::string str{__func__};
    add_test_case_to_json( str, tcase, suites_json);

    // test_format( val_seqs, op_seqs);
}
void test_delete_and_RotateRL(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{
    std::vector<int> val_seqs { 27, 35, 42, 38, 27};
    std::vector<int> op_seqs( val_seqs.size() - 1, static_cast<int>(AVL_INSERT));
    op_seqs.push_back( static_cast<int>(AVL_DEL));
    // test_format( val_seqs, op_seqs);    


    avl_test_case tcase{ { 27, 35,42, 38, 27}, {{AVL_INSERT, 4}, {AVL_DEL, 1}}, {35,38,42}};

    std::string str{ __func__};
    add_test_case_to_json( str, tcase, suites_json);
}

// void test_delete_and_adjust_twice()
// {
//     std::vector<int> val_seqs { 15, 9, 20, 7, 10, 17, 23, 8, 16, 18, 22, 24, 19, 10};
//     std::vector<int> op_seqs( val_seqs.size() - 1, static_cast<int>(AVL_INSERT));
//     op_seqs.push_back( static_cast<int>(AVL_DEL));
//     test_format( val_seqs, op_seqs);    
// }
void test_known_seqs(AVLTestManager& mng)
{
    std::vector<int> val_seqs{ 30, 20, 40, 10, 25, 5, 15, 35, 50, 60 };
    std::vector<int> op_seqs( val_seqs.size() , static_cast<int>(AVL_INSERT));

    std::vector<int> expect = val_seqs;
    std::sort( expect.begin(), expect.end());

    std::string str{__func__};

    avl_test_case tcase{ std::move( val_seqs ), {{AVL_INSERT, static_cast<uint32_t>(val_seqs.size())}},
        std::move( expect )
    };

    mng.addTestCase( str, tcase);

    // test_format( val_seqs, op_seqs);
}


void test_insert_ordered_seq(std::vector<avl_test_suite> &suites, nlohmann::json &suites_json)
{

    std::string str{ __func__ };
    int cnt = 10;
    for (int i = 1; i < 6; ++i, cnt *= 10) {
        std::vector<int> input( cnt , 0);
        std::iota(input.begin(), input.begin() + cnt, 0);

        std::vector<int> expect = input;    
        avl_test_case tcase{ std::move(input), {{AVL_INSERT, cnt}}, std::move(expect)};

        add_test_case_to_json( str, tcase, suites_json);
    }

}

void test_rand_all_insert_seqs( AVLTestManager& mng)
{
    std::string str{__func__};

    mng.removeTestSuite( str );

    int cnt = 10;
    for (int i = 1;i < 6; ++i, cnt *= 10) {
        std::vector<int> num_seq;

        std::default_random_engine egn{};

        std::mt19937 gen{42};
        std::uniform_int_distribution<int> dist(0, cnt);
        for (int i = 0;i < cnt;++i) {
            num_seq.push_back(dist(gen));
        }
        auto input = num_seq;

        std::sort( num_seq.begin(), num_seq.end());
        auto it  = std::unique( num_seq.begin(), num_seq.end());

        num_seq.resize( std::distance(num_seq.begin(), it));


        avl_test_case tcase{ std::move(input), {{AVL_INSERT, static_cast<uint32_t>(cnt)}}, std::move(num_seq)};

        mng.addTestCase( str, tcase);
    }   

}



int main()
{
    using namespace std;


    AVLTestManager fManager("test.json");


//     ns::Person p = {"cdindy",  "1234568", 21};

//    std::vector<int> arr{ data["array"].get<std::vector<int>>()};


//    for (auto v: arr) {
//     std::cout << v << " ";
//    }
//    std::cout << '\n';

//     json j = data.at("person");
//     ns::Person p0 {
//         j["name"].template get<string>(),
//         j["phone_number"].template get<std::string>(),
//         j["age"].template get<int>()
//     };

//     auto p1 = j.template get<ns::Person>();

    // if ( p0 == p1) {
    //     cout << "p0 p1 equal!\n";
    // }
    // else {
    //     cout << "p0 p1 not equal!\n";
    // }


    // json& persons_json = data.at("persons");
    // auto persons = persons_json.template get<std::vector<ns::Person>>();

    // for (auto &p: persons) {
    //     p.echo();
    // }

    // ns::Person pt{ "hao", "120", 32};
    // json wj = pt;

    // persons_json.push_back(wj);
    // std::ofstream op("test.json");
    // op << std::setw(4) << data << "\n";


    // wj["name"] = "wu";
    // wj["phone_number"] = "110";
    // wj["age"] = 23;


    // std::ofstream o("other.json");
    // o <<std::setw(4) << wj  << "\n";

    // p0.name_ = data.at("persons").at("name");
    // p0.age_   = data.at("persons").at("age").get<int>();
    // p0.phone_number_ = data.at("persons").at("phone_number").get<string>();

    //p.echo();
test_insert_noRotate(fManager);
test_known_seqs( fManager );

test_rand_all_insert_seqs( fManager );
    // test_insert_rrr( mSuites, suites_json);
    // test_insert_lll( mSuites, suites_json);
    // test_insert_same( mSuites, suites_json);
    // test_insert_lr( mSuites, suites_json);
    // test_insert_rl( mSuites, suites_json);
    // test_delete_and_RotateL( mSuites, suites_json);
    // test_delete_and_RotateR( mSuites, suites_json);
    // test_delete_and_RotateLR( mSuites, suites_json);
    // test_delete_and_RotateRL( mSuites, suites_json);

//     test_insert_ordered_seq( mSuites, suites_json);

    std::cout << "here \n" << std::endl;


//    test_delete_root( mSuites, suites_json);

    auto mSuites = fManager.getAllTestSuites();


    for ( auto &suite: mSuites) {
        AVLTestRunner::run_test_suite( suite );
    }

    return 0;
}